/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package serialassistant_;

import gnu.io.NoSuchPortException;
import gnu.io.PortInUseException;
import gnu.io.SerialPort;
import gnu.io.SerialPortEvent;
import gnu.io.UnsupportedCommOperationException;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.ResourceBundle;
import java.util.TooManyListenersException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javafx.application.Platform;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.geometry.Insets;
import javafx.scene.control.Alert;
import javafx.scene.control.Alert.AlertType;
import javafx.scene.control.Button;
import javafx.scene.control.ButtonBar.ButtonData;
import javafx.scene.control.ButtonType;
import javafx.scene.control.CheckBox;
import javafx.scene.control.ColorPicker;
import javafx.scene.control.ComboBox;
import javafx.scene.control.Dialog;
import javafx.scene.control.Label;
import javafx.scene.control.Menu;
import javafx.scene.control.MenuBar;
import javafx.scene.control.MenuItem;
import javafx.scene.control.RadioMenuItem;
import javafx.scene.control.TextArea;
import javafx.scene.control.TextField;
import javafx.scene.control.ToggleGroup;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.layout.GridPane;
import javafx.scene.paint.Color;
import javafx.scene.paint.Paint;
import javafx.scene.text.Font;
import javafx.scene.text.FontWeight;
import javafx.stage.FileChooser;
import javafx.stage.Stage;
import javax.swing.JFileChooser;
import serialassistant.model.SerialOpenAndClose;
import serialassistant.model.SerialPortUtil;
/**
 * FXML Controller class
 *
 * @author wxhntmy
 */
public class HomePageViewController implements Initializable {

    @FXML
    private MenuBar menubar;//菜单栏
    @FXML
    private Button btClearWindow;//清除窗口按键
    @FXML
    private Button btOpenFile;//打开文件按键
    @FXML
    private TextArea displayTextArea;//显示区
    @FXML
    private TextField fileTextField;//显示打开的文件地址
    @FXML
    private Button btSendFile;//发送文件按键
    @FXML
    private Button btSend;//小的发送按键
    @FXML
    private Button btClearSendBuff;//清空发送区按键
    @FXML
    private ComboBox<String> comList;//串口列表
    @FXML
    private Button btOpenSerial;//打开串口按键
    @FXML
    private Button btOpenSend;//大的发送按键
    @FXML
    private TextArea sendTextArea;//发送区
    @FXML
    private ComboBox<Integer> comBaudRate;//波特率列表
    @FXML
    private ComboBox<Integer> comDataBits;//数据位列表
    @FXML
    private ComboBox<String> comStopBits;//停止位列表
    @FXML
    private ComboBox<String> comParity;//校验方式列表
    @FXML
    private Label sendDataLength;//发送数据长度显示
    @FXML
    private Label receiveDataLength;//接收数据长度显示
    @FXML
    private Label serialUtilDisplay;//串口配置显示
    @FXML
    private CheckBox lineFeedCheckBox;//加回车换行
    @FXML
    private RadioMenuItem displayBufferUpperLimit200K;//显示缓冲上限200K
    @FXML
    private RadioMenuItem displayBufferUpperLimit500K;//显示缓冲上限500K
    @FXML
    private RadioMenuItem displayBufferUpperLimit1M;//显示缓冲上限1M
    @FXML
    private RadioMenuItem displayBufferUpperLimit2M;//显示缓冲上限2M
    @FXML
    private RadioMenuItem displayBufferUpperLimit5M;//显示缓冲上限5M
    @FXML
    private RadioMenuItem enterSend;//回车后发送
    @FXML
    private RadioMenuItem sendButtonValue;//发送按键键值
    @FXML
    private RadioMenuItem displayButtonValue;//本地回显按键键值
    @FXML
    private RadioMenuItem sendEnterAndNewlineCharacter;//发送回车0x0a后自动附加换行符0x0d
    @FXML
    private MenuItem refreshSerialPortList;//刷新串口列表
    @FXML
    private MenuItem fontColorSize;//设置字体/颜色/大小
    @FXML
    private MenuItem backgroundColor;//背景颜色
    @FXML
    private MenuItem displayBufferUpperLimitHelp;//显示缓冲上限帮助
    @FXML
    private MenuItem sendHelp;//发送帮助
    @FXML
    private Menu multipleStrings;//多字符串
    @FXML
    private Menu gadget;//小工具
    @FXML
    private MenuItem help;//帮助
    @FXML
    private MenuItem contactAuthor;
    
    //显示缓冲上限单选框
    ToggleGroup displayBufferUpperLimitGroup = new ToggleGroup();
    
    private List<String> portList = null;//串口列表
    private SerialOpenAndClose parameter = new SerialOpenAndClose();
    private SerialPort serialPort = null;//串口对象
    private Integer sendLength = 0;//已发送的数据长度
    private Integer receiveLength = 0;//已接收的数据长度
    private String  serialPortName = "COM1";//默认串口名字
    private String  serialPortStatus = "已关闭";//默认串口状态
    
    private File f = null;//f为选择到的文件
    
    private long handledisplayBufferUpperLimit = 102400;//默认显示缓冲上限100K
    
    /**
     * Initializes the controller class.
     * @param url 无
     * @param rb 无
     */
    @Override
    public void initialize(URL url, ResourceBundle rb) {
        // TODO
        //加入combobox列表
        comBaudRate.getItems().addAll(110, 300, 600, 1200, 2400, 4800, 9600, 14400, 19200, 38400, 56000, 57600, 115200, 128000, 230400, 256000, 460800, 500000, 512000, 600000, 750000, 921600, 1000000, 1500000, 2000000);
        comDataBits.getItems().addAll(5, 6, 7, 8);
        comStopBits.getItems().addAll("1", "1.5", "2");
        comParity.getItems().addAll("None", "Odd", "Even", "Mark", "Space");
        if(getSystemPortList()){
            for(int i = 0; i < portList.size(); i++){
                comList.getItems().add(portList.get(i));
            }
        }
        
        //将单选框加入到同一个组里，同一个组里的单选键只能被选中一个
        displayBufferUpperLimitGroup.getToggles().add(displayBufferUpperLimit200K);
        displayBufferUpperLimitGroup.getToggles().add(displayBufferUpperLimit500K);
        displayBufferUpperLimitGroup.getToggles().add(displayBufferUpperLimit1M);
        displayBufferUpperLimitGroup.getToggles().add(displayBufferUpperLimit2M);
        displayBufferUpperLimitGroup.getToggles().add(displayBufferUpperLimit5M);
        
        //设置显示区自动换行
        displayTextArea.setWrapText(true);
        
        //设置串口初始值，防止空指针
        comBaudRate.setValue(9600);
        comDataBits.setValue(8);
        comStopBits.setValue("1");
        comParity.setValue("None");
        comList.setValue("COM1");
        receiveDataLength.setText("Receive: " + receiveLength);//显示初始接收数据长度
        sendDataLength.setText("Send: " + sendLength);//显示初始发送数据长度
        //显示串口初始值
        serialUtilDisplay.setText(serialPortName + "   " + serialPortStatus + "   " + comBaudRate.getValue()
            + "bps, " + comDataBits.getValue() + ", " + comStopBits.getValue() + ", " + comParity.getValue());
        
        //默认按Ctrl+Enter发送
        enterSend.setSelected(true);
    }    

    @FXML
    /**
     * 大的发送按键
     */
    private void handlebtOpenSendAction(ActionEvent event) {
        if(btOpenSerial.getText().equals("关闭串口")){//打开了串口才能发送
            String s = sendTextArea.getText();
            if(!s.isEmpty()){//发送区不为空才能发送
                if(lineFeedCheckBox.isSelected()){//选中了加换行符发送
                    s = s + "\r\n";
                }
                if(sendEnterAndNewlineCharacter.isSelected()){//选中了发送回车0x0a后自动附加换行符0x0d
                    if(s.substring(s.length() - 1, s.length()).equals(0x0D)){
                        s = s + 0x0A;
                    }
                }
                byte[] bytes = s.getBytes();//把字符串转为二进制数组
                sendLength = sendLength + bytes.length;//记录发送的数据长度
                SerialPortUtil.sendData(serialPort, bytes);//发送数据
                if(displayTextArea.getText().length() < handledisplayBufferUpperLimit){
                    displayTextArea.appendText("发→：" + s + "\n");
                    sendDataLength.setText("Send: " + sendLength);//显示更新的发送的数据长度
                }
                else{
                    displayTextArea.appendText("已达显示区缓冲上限！\n");
                }
            }
            else{
                Alert alert = new Alert(AlertType.WARNING);
                alert.setTitle("警告");//对话框标题
                alert.setHeaderText(null);
                alert.setContentText("发送内容不能为空！");//对话框内容
                alert.showAndWait();
            }
        }
        else{
            Alert alert = new Alert(AlertType.WARNING);
            alert.setTitle("警告");//对话框标题
            alert.setHeaderText(null);
            alert.setContentText("串口还没有打开！");//对话框内容
            alert.showAndWait();
        }
    }
    
    /**
     * 串口监听 
     */
    public void getSerialPortActionEvent(){
        try {
            SerialPortUtil.setListenerToSerialPort(serialPort, events -> {
                //数据通知
                if (events.getEventType() == SerialPortEvent.DATA_AVAILABLE) {
                    byte[] bytess = SerialPortUtil.readData(serialPort);//读串口数据
                    receiveLength = receiveLength + bytess.length;//更新接收的数据长度
                    if(displayTextArea.getText().length() < handledisplayBufferUpperLimit){
                        displayTextArea.appendText("收←：" + new String(bytess) + "\n");//显示接收的数据
                        /**
                        * Java FX程序不允许用户在FX application thread线程外更新界面的组件信息
                        * 即所有的更新界面组件的信息的代码，都应该在在FX application thread线程中执行。
                        * 用Platform类的runLater方法可以解决该问题
                        * 该方法可以将参数runnable将要执行的代码，交给FX application thread线程执行，这样问题就解决了
                        */
                       Platform.runLater(()->{
                           //任何更新界面组件信息的代码
                           receiveDataLength.setText("Receive: " + receiveLength);//显示更新的接收的数据长度
                       });
                    }
                    else{
                        displayTextArea.appendText("已达显示区缓冲上限！\n");
                    }
                }
            });
        } catch (TooManyListenersException ex) {
            Logger.getLogger(HomePageViewController.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
    /**
     * 获取串口列表
     * @return 如果串口列表不为空，返回true
     */
    public boolean getSystemPortList() {
        portList = SerialPortUtil.getSerialPortList();//获取系统可用的串口列表
        return !portList.isEmpty();//如果串口列表不为空，返回true
    }

    @FXML
    /**
     * 清除窗口按键
     */
    private void handlebtClearWindowAction(ActionEvent event) {
        displayTextArea.setText("");//清空显示区
        fileTextField.setText("");//清空打开的文件的绝对地址
        receiveLength = 0;//清空接收的数据长度
        sendLength = 0;//清空发送的数据长度
        receiveDataLength.setText("Receive: " + receiveLength);//显示新的接收的数据长度
        sendDataLength.setText("Send: " + sendLength);//显示新的发送的数据长度
    }

    @FXML
    /**
     * 打开文件按键
     */
    private void handlebtOpenFileAction(ActionEvent event) {
        Stage stage = new Stage();//创建FileChooser需要一个新的Stage
        FileChooser fileChooser = new FileChooser();//新建FileChooser选择文件
        fileChooser.setTitle("打开文件");
        fileChooser.getExtensionFilters().addAll(
            new FileChooser.ExtensionFilter("All Files", "*.*"));
        f = fileChooser.showOpenDialog(stage);
        fileTextField.setText(f.getAbsolutePath());//显示选择到的文件的绝对地址
        
        //文件内容的读取
        InputStreamReader reader = null;//InputStreamReader类是从字节流到字符流的桥接器：它使用指定的字符集读取字节并将它们解码为字符。
        try {
            reader = new InputStreamReader(new FileInputStream(f), "GBK");//以GBK的字符集打开文件
        } catch (FileNotFoundException ex) {
            Logger.getLogger(HomePageViewController.class.getName()).log(Level.SEVERE, null, ex);
        } catch (UnsupportedEncodingException ex) {
            Logger.getLogger(HomePageViewController.class.getName()).log(Level.SEVERE, null, ex);
        }
        BufferedReader bfreader = new BufferedReader(reader);//BufferedReader类从字符输入流中读取文本并缓冲字符，以便有效地读取字符，数组和行
        String line;//缓冲BufferedReader读取的每一行字符
        try {
            displayTextArea.setText("");
            displayTextArea.appendText("文件大小:" + f.length() + "字节，下面是预览的前" + f.length() + "字节内容：\n");
            while((line = bfreader.readLine()) != null) {//包含该行内容的字符串，不包含任何行终止符，如果已到达流末尾，则返回 null
                if(displayTextArea.getText().length() < handledisplayBufferUpperLimit){
                    displayTextArea.appendText(line + "\n");
                }
                else{
                    displayTextArea.appendText("已达显示区缓冲上限！\n");
                }
            }
        } catch (IOException ex) {
            Logger.getLogger(HomePageViewController.class.getName()).log(Level.SEVERE, null, ex);
        }
        try {
            reader.close();//关闭InputStreamReader
            bfreader.close();//关闭BufferedReader
        } catch (IOException ex) {
            Logger.getLogger(HomePageViewController.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    @FXML
    /**
     * 发送文件按键
     */
    private void handlebtSendFileAction(ActionEvent event) {
        //文件内容的读取
        if(f != null){
            InputStreamReader reader = null;
            try {
                reader = new InputStreamReader(new FileInputStream(f),"GBK");//以GBK的字符集打开文件
            } catch (FileNotFoundException ex) {
                Logger.getLogger(HomePageViewController.class.getName()).log(Level.SEVERE, null, ex);
            } catch (UnsupportedEncodingException ex) {
                Logger.getLogger(HomePageViewController.class.getName()).log(Level.SEVERE, null, ex);
            }
            BufferedReader bfreader = new BufferedReader(reader);//读取
            String line;//按行读取文件数据
            try {
                displayTextArea.setText("");
                displayTextArea.appendText("文件大小: " + f.length() + "\n");
                displayTextArea.appendText("波特率 " + parameter.getBaudRate() + "bps\n");
                displayTextArea.appendText("请稍后...\n");
                while((line = bfreader.readLine()) != null) {//包含该行内容的字符串，不包含任何行终止符，如果已到达流末尾，则返回 null
                    line = line + "\n";
                    byte[] bytes = line.getBytes();
                    SerialPortUtil.sendData(serialPort, bytes);//按行发送数据
                    sendLength = sendLength + bytes.length;//更新实际发送的数据长度
                    sendDataLength.setText("Send: " + sendLength);//显示更新后的数据长度
                }
                displayTextArea.appendText("发送完毕！\n");
            } catch (IOException ex) {
                Logger.getLogger(HomePageViewController.class.getName()).log(Level.SEVERE, null, ex);
            }
            try {
                reader.close();
            } catch (IOException ex) {
                Logger.getLogger(HomePageViewController.class.getName()).log(Level.SEVERE, null, ex);
            }
            try {
                bfreader.close();
            } catch (IOException ex) {
                Logger.getLogger(HomePageViewController.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
    }

    @FXML
    /**
     * 小的发送按键
     */
    private void handlebtSendAction(ActionEvent event) {
        handlebtOpenSendAction(event);
    }

    @FXML
    /**
     * 清除发送区按键
     */
    private void handlebtClearSendBuffAction(ActionEvent event) {
        sendTextArea.setText("");
    }

    @FXML
    /**
     * 打开串口按键
     */
    private Integer handlebtOpenSerialAction(ActionEvent event) {
        parameter.setSerialPortName(comList.getValue());//串口名
        parameter.setBaudRate(comBaudRate.getValue());//波特率
        parameter.setDataBits(comDataBits.getValue());//数据位
        
        Integer stopBit = 0;
        if(comStopBits.getValue().equals("1")){
            stopBit = SerialPort.STOPBITS_1;
        }
        if(comStopBits.getValue().equals("1.5")){
            stopBit = SerialPort.STOPBITS_1_5;
        }
        if(comStopBits.getValue().equals("2")){
            stopBit = SerialPort.STOPBITS_2;
        }
        parameter.setStopBits(stopBit);//停止位
        
        Integer parity = -1;
        if(comParity.getValue().equals("None")){
            parity = SerialPort.PARITY_NONE;
        }
        if(comParity.getValue().equals("Odd")){
            parity = SerialPort.PARITY_ODD;
        }
        if(comParity.getValue().equals("Even")){
            parity = SerialPort.PARITY_EVEN;
        }
        if(comParity.getValue().equals("Mark")){
            parity = SerialPort.PARITY_MARK;
        }
        if(comParity.getValue().equals("Space")){
            parity = SerialPort.PARITY_SPACE;
        }
        parameter.setParity(parity);//校验方式
        
        Integer status = 0;
        if(btOpenSerial.getText().equals("打开串口")){
            try {
                serialPort = SerialPortUtil.openSerialPort(parameter);//打开串口
                serialPortName = parameter.getSerialPortName();
                serialPortStatus = "已开启";
                serialUtilDisplay.setText(serialPortName + "   " + serialPortStatus + "   " + parameter.getBaudRate()
                    + "bps, " + parameter.getDataBits() + ", " + comStopBits.getValue() + ", " + comParity.getValue());
                getSerialPortActionEvent();//打开串口之后开启串口监听
                status = 1;
            } catch (NoSuchPortException ex) {
                status = 2;//对应串口不存在
                Logger.getLogger(HomePageViewController.class.getName()).log(Level.SEVERE, null, ex);
            } catch (PortInUseException ex) {
                status = 3;//串口在使用中
                Logger.getLogger(HomePageViewController.class.getName()).log(Level.SEVERE, null, ex);
            } catch (UnsupportedCommOperationException ex) {
                status = 4;//不支持操作
                Logger.getLogger(HomePageViewController.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
        
        if(status == 1) {
            btOpenSerial.setTextFill(Paint.valueOf("red"));//设置字体颜色为红色
            btOpenSerial.setFont(Font.font("System", FontWeight.BOLD, 24));//字体加粗
            btOpenSerial.setText("关闭串口");
            return 1;//return中断程序
        }
        if(status == 2){
            Alert alert = new Alert(AlertType.WARNING);
            alert.setTitle("警告");//对话框标题
            alert.setHeaderText(null);
            alert.setContentText("串口" + parameter.getSerialPortName() + "不存在！");//对话框内容
            alert.showAndWait();
            return 2;
        }
        if(status == 3){
            Alert alert = new Alert(AlertType.WARNING);
            alert.setTitle("警告");//对话框标题
            alert.setHeaderText(null);
            alert.setContentText("串口" + parameter.getSerialPortName() + "正在使用中！");//对话框内容
            alert.showAndWait();
            return 3;
        }
        if(status == 4){
            Alert alert = new Alert(AlertType.WARNING);
            alert.setTitle("警告");//对话框标题
            alert.setHeaderText(null);
            alert.setContentText("串口" + parameter.getSerialPortName() + "不支持操作！");//对话框内容
            alert.showAndWait();
            return 4;
        }
        if(btOpenSerial.getText().equals("关闭串口")){
            btOpenSerial.setTextFill(Paint.valueOf("black"));//设置字体颜色为黑色
            btOpenSerial.setFont(Font.font("System", FontWeight.NORMAL, 24));//字体粗细正常
            btOpenSerial.setText("打开串口");
            closeSerialPort();//关闭串口
            return 5;
        }
        return 6;
    }
    /**
     * 关闭串口
     */
    public void closeSerialPort() {
        SerialPortUtil.closeSerialPort(serialPort);//关闭已打开的串口
        serialPortStatus = "已关闭";
        //更新串口状态
        serialUtilDisplay.setText(serialPortName + "   " + serialPortStatus + "   " + parameter.getBaudRate()
            + "bps, " + parameter.getDataBits() + ", " + comStopBits.getValue() + ", " + comParity.getValue());
    }
    

    @FXML
    /**
     * 串口列表combobox点击事件
     */
    private void handlecomListAction(ActionEvent event) {
        serialUtilDisplay.setText(comList.getValue() + "   " + serialPortStatus + "   " + comBaudRate.getValue()
            + "bps, " + comDataBits.getValue() + ", " + comStopBits.getValue() + ", " + comParity.getValue());
        parameter.setSerialPortName(comList.getValue());//串口
    }

    @FXML
    /**
     * 波特率列表combobox点击事件
     */
    private void handlecomBaudRateAction(ActionEvent event) {
        serialUtilDisplay.setText(comList.getValue() + "   " + serialPortStatus + "   " + comBaudRate.getValue()
            + "bps, " + comDataBits.getValue() + ", " + comStopBits.getValue() + ", " + comParity.getValue());
        parameter.setBaudRate(comBaudRate.getValue());//波特率
    }

    @FXML
    /**
     * 数据位列表combobox点击事件
     */
    private void handlecomDataBitsAction(ActionEvent event) {
        serialUtilDisplay.setText(comList.getValue() + "   " + serialPortStatus + "   " + comBaudRate.getValue()
            + "bps, " + comDataBits.getValue() + ", " + comStopBits.getValue() + ", " + comParity.getValue());
        parameter.setDataBits(comDataBits.getValue());//数据位
    }

    @FXML
    /**
     * 停止位列表combobox点击事件
     */
    private void handlecomStopBitsAction(ActionEvent event) {
        serialUtilDisplay.setText(comList.getValue() + "   " + serialPortStatus + "   " + comBaudRate.getValue()
            + "bps, " + comDataBits.getValue() + ", " + comStopBits.getValue() + ", " + comParity.getValue());
        Integer stopBit = 0;
        if(comStopBits.getValue().equals("1")){
            stopBit = SerialPort.STOPBITS_1;
        }
        if(comStopBits.getValue().equals("1.5")){
            stopBit = SerialPort.STOPBITS_1_5;
        }
        if(comStopBits.getValue().equals("2")){
            stopBit = SerialPort.STOPBITS_2;
        }
        parameter.setStopBits(stopBit);//停止位
    }

    @FXML
    /**
     * 校验方式列表combobox点击事件
     */
    private void handlecomParityAction(ActionEvent event) {
        serialUtilDisplay.setText(comList.getValue() + "   " + serialPortStatus + "   " + comBaudRate.getValue()
            + "bps, " + comDataBits.getValue() + ", " + comStopBits.getValue() + ", " + comParity.getValue());
        Integer parity = -1;
        if(comParity.getValue().equals("None")){
            parity = SerialPort.PARITY_NONE;
        }
        if(comParity.getValue().equals("Odd")){
            parity = SerialPort.PARITY_ODD;
        }
        if(comParity.getValue().equals("Even")){
            parity = SerialPort.PARITY_EVEN;
        }
        if(comParity.getValue().equals("Mark")){
            parity = SerialPort.PARITY_MARK;
        }
        if(comParity.getValue().equals("Space")){
            parity = SerialPort.PARITY_SPACE;
        }
        parameter.setParity(parity);//校验方式
    }

    @FXML
    /**
     * 菜单：更新串口列表点击事件
     */
    private void handlerefreshSerialPortListAction(ActionEvent event) {
        comList.getItems().clear();//刷新串口列表之前先清空原来的串口列表
        if(getSystemPortList()){//获取系统可用的串口列表
            if(comList != null){
                for(int i = 0; i < portList.size(); i++){
                    comList.getItems().add(portList.get(i));//更新串口列表
                }
            }
        }
        comList.setValue("COM1");
    }

    @FXML
     /**
     * 菜单：字体/颜色/大小点击事件
     */
    private void handlefontColorSizeAction(ActionEvent event) {
        //设置dialog的返回类型，dialog返回的是List类型.
        Dialog<List> dialog;
        dialog = new Dialog<>();
        dialog.setTitle("设置字体/颜色/大小");//dialog标题
        dialog.setHeaderText(null);

        //设置按键类型.
        ButtonType confirmButtonType = new ButtonType("确定", ButtonData.OK_DONE);
        dialog.getDialogPane().getButtonTypes().addAll(confirmButtonType, ButtonType.CANCEL);

        //设置网格布局窗格GridPane
        GridPane grid = new GridPane();
        grid.setHgap(10);//列之间水平间隙的宽度
        grid.setVgap(10);//行之间垂直间隙的高度。
        grid.setPadding(new Insets(20, 150, 10, 10));//构造具有四个不同偏移的新插入实例。

        ComboBox<String> comFont = new ComboBox();//字体列表
        comFont.setValue(displayTextArea.getFont().getFamily());//获取当前的字体
        List<String> mylist = Font.getFamilies();//获取系统已安装的字体
	for(int i = 0; i < mylist.size(); i++) {
            comFont.getItems().add(mylist.get(i));
	}
        
        final ColorPicker colorPicker = new ColorPicker();//颜色选择器
        colorPicker.setValue(Color.CORAL);
        
        ComboBox<Integer> comSize = new ComboBox();//字体大小列表
        Double fontSize = displayTextArea.getFont().getSize();//获取当前的字体大小
        comSize.setValue(fontSize.intValue());
        comSize.getItems().addAll(15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30);

        grid.add(new Label("字体："), 0, 0);
        grid.add(comFont, 1, 0);
        grid.add(new Label("颜色："), 0, 1);
        grid.add(colorPicker, 1, 1);
        grid.add(new Label("字体大小："), 0, 2);
        grid.add(comSize, 1, 2);

        //把网格布局窗格添加到dialog里
        dialog.getDialogPane().setContent(grid);
        //单击确定按钮时，将结果转换为List返回。
        dialog.setResultConverter(dialogButton -> {
            if (dialogButton == confirmButtonType) {
                List list = new ArrayList();
                list.add(comFont.getValue());
                list.add(colorPicker.getValue());
                list.add(comSize.getValue());
                return list;
            }
            return null;
        });
        Optional<List> result = dialog.showAndWait();//接收dialog返回的List

        result.ifPresent(list -> {
            //设置字体和字体大小
            displayTextArea.setFont(Font.font(list.get(0).toString(), FontWeight.NORMAL, Integer.parseInt(list.get(2).toString())));
            //设置字体颜色
            displayTextArea.setStyle("-fx-text-fill: #" + list.get(1).toString().substring(2, list.get(1).toString().length()));
        });
    }

    @FXML
     /**
     * 菜单：背景颜色点击事件
     */
    private void handlebackgroundColorAction(ActionEvent event) {
        //设置dialog的返回类型，dialog返回的是List类型.
        Dialog<List> dialog;
        dialog = new Dialog<>();
        dialog.setTitle("设置背景颜色");//dialog标题
        dialog.setHeaderText(null);

        //设置按键类型.
        ButtonType confirmButtonType = new ButtonType("确定", ButtonData.OK_DONE);
        dialog.getDialogPane().getButtonTypes().addAll(confirmButtonType, ButtonType.CANCEL);

        //设置网格布局窗格GridPane
        GridPane grid = new GridPane();
        grid.setHgap(10);//列之间水平间隙的宽度
        grid.setVgap(10);//行之间垂直间隙的高度。
        grid.setPadding(new Insets(20, 150, 10, 10));//构造具有四个不同偏移的新插入实例。

        final ColorPicker colorPicker = new ColorPicker();//颜色选择器
        colorPicker.setValue(Color.CORAL);

        grid.add(new Label("背景颜色："), 0, 0);
        grid.add(colorPicker, 1, 0);

        //把网格布局窗格添加到dialog里
        dialog.getDialogPane().setContent(grid);

        //单击确定按钮时，将结果转换为List返回。
        dialog.setResultConverter(dialogButton -> {
            if (dialogButton == confirmButtonType) {
                List list = new ArrayList();
                list.add(colorPicker.getValue());
                return list;
            }
            return null;
        });

        Optional<List> result = dialog.showAndWait();//接收dialog返回的List

        result.ifPresent(list -> {
            displayTextArea.setStyle("-fx-background-color: #" + list.get(0).toString().substring(2, list.get(0).toString().length()));
        });

    }

    @FXML
     /**
     * 菜单：显示缓冲上限200K点击事件
     */
    private void handledisplayBufferUpperLimit200KAction(ActionEvent event) {
        handledisplayBufferUpperLimit = 1024 * 200;
    }

    @FXML
    /**
     * 菜单：显示缓冲上限500K点击事件
     */
    private void handledisplayBufferUpperLimit500KAction(ActionEvent event) {
        handledisplayBufferUpperLimit = 1024 * 500;
    }

    @FXML
    /**
     * 菜单：显示缓冲上限1M点击事件
     */
    private void handledisplayBufferUpperLimit1MAction(ActionEvent event) {
        handledisplayBufferUpperLimit = 1024 * 1024 * 1;
    }

    @FXML
    /**
     * 菜单：显示缓冲上限2M点击事件
     */
    private void handledisplayBufferUpperLimit2MAction(ActionEvent event) {
        handledisplayBufferUpperLimit = 1024 * 1024 * 2;
    }

    @FXML
    /**
     * 菜单：显示缓冲上限5M点击事件
     */
    private void handledisplayBufferUpperLimit5MAction(ActionEvent event) {
        handledisplayBufferUpperLimit = 1024 * 1024 * 5;
    }

    @FXML
    /**
     * 菜单：显示缓冲上限帮助点击事件
     */
    private void handledisplayBufferUpperLimitHelpAction(ActionEvent event) {
        Alert alert = new Alert(AlertType.INFORMATION);
        alert.setTitle("发送帮助");//对话框标题
        alert.setHeaderText(null);
        alert.setContentText("需要帮助请联系作者！");//对话框内容
        alert.showAndWait();
    }

    @FXML
    /**
     * 菜单：回车发送点击事件
     */
    private void handleenterSendAction(ActionEvent event) {
        if(sendButtonValue.isSelected() && displayButtonValue.isSelected()){
            sendButtonValue.setSelected(false);
        }
    }
    @FXML
    /**
     * 输入区键值检测
     */
    private void getButtonValueAction(KeyEvent event) {
        //检测到回车发送
        if(enterSend.isSelected()){         //菜单：回车发送被选中
            if(event.getCode() == KeyCode.ENTER){   //检测键值是否为回车键
                if(btOpenSerial.getText().equals("关闭串口")){//打开了串口才能发送
                    String s = sendTextArea.getText();
                    if(!s.isEmpty()){//发送区不为空才能发送
                        if(lineFeedCheckBox.isSelected()){//选中了加换行符发送
                            s = s + "\r\n";
                        }
                        byte[] bytes = s.getBytes();//把字符串转为二进制数组
                        sendLength = sendLength + bytes.length;//记录发送的数据长度
                        SerialPortUtil.sendData(serialPort, bytes);//发送数据
                        if(displayTextArea.getText().length() < handledisplayBufferUpperLimit){
                            displayTextArea.appendText("发→：" + s + "\n");
                            sendDataLength.setText("Send: " + sendLength);//显示更新的发送的数据长度
                        }
                        else{
                            displayTextArea.appendText("已达显示区缓冲上限！\n");
                        }
                    }
                }
            }
        }
        //按键立即发送该键值
        if(sendButtonValue.isSelected()){         //菜单：
            KeyCode code = event.getCode();
            if(btOpenSerial.getText().equals("关闭串口")){//打开了串口才能发送
                String s = code.toString();
                if(lineFeedCheckBox.isSelected()){//选中了加换行符发送
                    s = s + "\r\n";
                }
                byte[] bytes = s.getBytes();//把字符串转为二进制数组
                sendLength = sendLength + bytes.length;//记录发送的数据长度
                SerialPortUtil.sendData(serialPort, bytes);//发送数据
                if(displayTextArea.getText().length() < handledisplayBufferUpperLimit){
                    displayTextArea.appendText("发→：" + s + "\n");
                    sendDataLength.setText("Send: " + sendLength);//显示更新的发送的数据长度
                }
                else{
                    displayTextArea.appendText("已达显示区缓冲上限！\n");
                }
            }
        }
    }

    @FXML
    /**
     * 菜单：发送键值点击事件
     */
    private void handlesendButtonValueAction(ActionEvent event) {
        if(sendButtonValue.isSelected() && displayButtonValue.isSelected()){
            enterSend.setSelected(false);
        }
    }

    @FXML
    /**
     * 菜单：本地回显键值点击事件
     */
    private void handledisplayButtonValueAction(ActionEvent event) {
        if(sendButtonValue.isSelected() && displayButtonValue.isSelected()){
            enterSend.setSelected(false);
        }
    }

    @FXML
    /**
     * 菜单：发送回车0x0a后自动附加换行符0x0d点击事件
     */
    private void handlesendEnterAndNewlineCharacterAction(ActionEvent event) {
    }


    @FXML
    /**
     * 菜单：发送帮助点击事件
     */
    private void handlesendHelpAction(ActionEvent event) {
        Alert alert = new Alert(AlertType.INFORMATION);
        alert.setTitle("发送帮助");//对话框标题
        alert.setHeaderText(null);
        alert.setContentText("需要帮助请联系作者！");//对话框内容
        alert.showAndWait();
    }

    @FXML
    /**
     * 菜单：多字符串点击事件
     */
    private void handlemultipleStringsAction(ActionEvent event) {
    }

    @FXML
    /**
     * 菜单：小工具点击事件
     */
    private void handlegadgetAction(ActionEvent event) {
    }

    @FXML
    /**
     * 菜单：帮助点击事件
     */
    private void handlehelpAction(ActionEvent event) {
        Alert alert = new Alert(AlertType.INFORMATION);
        alert.setTitle("帮助");//对话框标题
        alert.setHeaderText(null);
        alert.setContentText("需要帮助请联系作者！");//对话框内容
        alert.showAndWait();
    }

    @FXML
    /**
     * 菜单：联系作者点击事件
     */
    private void handlecontactAuthorAction(ActionEvent event) {
        Alert alert = new Alert(AlertType.INFORMATION);
        alert.setTitle("联系作者");//对话框标题
        alert.setHeaderText(null);
        alert.setContentText("邮箱：wxhntmy@163.com（非提交bug勿扰）");//对话框内容
        alert.showAndWait();
    }
}
